package com.google.code.sysarch.v4.graphical;

import java.awt.Point;
import java.util.ArrayList;

import org.lwjgl.opengl.GL11;

import com.google.code.sysarch.v4.vrml_loader.VRML_Model;
import com.google.code.sysarch.v4.vrml_loader.VRML_Shape;

public class Grid {

	public static final int COLOR_NONE = -1;	
	public static final int COLOR_WHITE = 0;
	public static final int COLOR_GREEN = 1;
	public static final int COLOR_RED = 2;
	public static final int COLOR_ORANGE = 3;
	public static final int COLOR_BLUE = 4;
	public static final int COLOR_DEAD = 5;

	public static final int ORIENTATION_X = 0;
	public static final int ORIENTATION_Y = 1;
	public static final int ORIENTATION_SWITCH = 1;
	public static final int FREE = 0;
	public static final int OUTSIDE = 0xFFFFFF;
	public static final int DESTROYED = -2;
	private static final int[] angles = { 90, 0 };
	private int[] steps = { 1, 666 }; // second value is grid specific

	private static VRML_Shape field;
	private static VRML_Model[] ships;
	private static Texture[] gridtex;
	private static boolean touching = false;
	private static int shippool[];
	private static Button[] button_ship;
	
	private ArrayList<Ship> shiplist;
	private ArrayList<Integer> freenames;
	private Ship hovership;	
	private int[] gridstate;
	private int[] occupation;
	private int rows;
	private int columns;
	private float size = 16;
	private int gridoffset;
	private float x, z;
	private int gridhover;
	private int gridhovercolor;
	private boolean gridhoveroverrride;
	
	Grid(int rows, int columns) {
		this.rows = rows;
		this.columns = columns;
		this.gridoffset = 0;	
		shiplist = new ArrayList<Ship>();
		freenames = new ArrayList<Integer>();
		for (int i=1; i<=shippool[0]; i++) freenames.add(i);
		gridstate = new int[columns*(rows+4)];
		occupation = new int[columns*(rows+4)];
		hovership = new Ship(ships[0], -1, -1, 1, ORIENTATION_X);
		steps[ORIENTATION_Y] = rows+4;
		clearGrid();
		clearShips();
		if (field == null) createField();
	}
	
	public static void setShips(VRML_Model[] ships) {
		Grid.ships = ships;
	}
	
	public static void setShipPool(int[] pool) {
		Grid.shippool = pool;
	}
	
	public static void setTouchingAllowed(boolean on) {
		Grid.touching = on;
	}
	
	public static void setButtons(Button[] buttons) {
		Grid.button_ship = buttons;
	}
	
	public int toGridCoord(int x, int y) {
		return y*(rows+4)+x+2;
	}
	
	public Point toGameCoord(int c) {
		return new Point(c%(rows+4) - 2, c/(rows+4));
	}
	
	public void setSize(float size) {
		this.size = size;
	}
	
	public void setGrid(int coord, int color) {
		gridstate[coord] = color;
	}
	
	public int getGrid(int coord) {
		if (coord >= 0 && coord < gridstate.length) {
			return gridstate[coord];
		} else {
			return COLOR_NONE;
		}
	}
	
	public float getRealWidth() {
		return this.size * this.rows;
	}
	
	public float getRealHeight() {
		return this.size * this.columns;
	}	
	
	public void translate(float x, float z) {
		this.x = x;
		this.z = z;
	}
	
	public void setGridOffset(int o) {
		this.gridoffset = o;
	}
	
	public int getGridMinOffset() {
		return this.gridoffset;
	}
	
	public int getGridMaxOffset() {
		return this.gridoffset + this.columns * (this.rows + 4);
	}
	
	public int getShipMinOffset() {		
		return this.gridoffset + this.columns * (this.rows + 4) + 1;
	}
	
	public int getShipMaxOffset() {		
		return this.gridoffset + this.columns * (this.rows + 4) + shippool[0];
	}	
	
	public boolean allShipsDeployed() {
		return (shiplist.size() >= shippool[0]) ? true : false;
	}
	
	public int getHoverOrientation() {
		return hovership.getOrientation();
	}
	
	public int getHoverLength() {
		return hovership.getLength();
	}
	
	public void setGridHover(int coord, int color, boolean override) {
		gridhover = coord;
		gridhovercolor = color;
		gridhoveroverrride = override;
	}
	
	public void setHoverCoord(int coord) {
		if (coord >= 0) {
			Point pos = toGameCoord(coord);
			hovership.setGridCoords(pos.x, pos.y);
			updateHoverGrid();
		} else {
			hovership.setGridCoords(-1, -1);
			clearGrid();
		}		
	}
	
	public void setHoverLength(int length) {
		hovership.setLength(length);
		if (length > 0) {
			hovership.setModel(ships[length-1]);
			for (int i=0; i<6; i++) button_ship[i].setTexture(0);
			button_ship[length-1].setTexture(1);
		}		
		updateHoverGrid();
	}
	
	public void setHoverOrientation(int orientation) {
		hovership.setOrientation(orientation);
		updateHoverGrid();
	}
	
	public void updateHoverGrid() {
		if (hovership.getX() < 0 || hovership.getY() < 0 || hovership.getLength() == 0) return;
		int coord = toGridCoord(hovership.getX(), hovership.getY());
		boolean allred = false;		
		
		for (int i=hovership.getLength()-1; i>=0; i--) {
			int c = coord + i*steps[hovership.getOrientation()];
			if (c < 0 || c >= gridstate.length) {
				allred = true;
			} else {
				if (occupation[c] == FREE && !allred) {
					gridstate[c] = COLOR_GREEN;
				} else {
					gridstate[c] = COLOR_RED;
					if (occupation[c] == OUTSIDE) allred = true;
				}
			}
		}
	}
	
	public void clearGrid() {
		for (int i=0; i<gridstate.length; i++) {
			gridstate[i] = COLOR_WHITE;
		}
	}
	
	public void clearShips() {
		shiplist.clear();
		for (int i=0; i<occupation.length; i++) {
			if ((i+2)%(rows+4) < 4) {
				occupation[i] = OUTSIDE;
			} else {
				occupation[i] = FREE;
			}
		}
	}
	
	public boolean shipAvailable(int length) {
		int counter = 0;
		for (int i=0; i<this.shiplist.size(); i++) {
			if (shiplist.get(i).getLength() == length) {
				counter++;
			}
		}
		if (counter < shippool[length]) {
			return true;
		} else {
			return false;
		}
	}
	
	public boolean placeShip() {
		Ship s = placeShip(hovership.getModel(), hovership.getX(), hovership.getY(), hovership.getLength(), hovership.getOrientation());
		if (s != null) {
			return true;
		} else {
			return false;
		}
	}
	
	public Ship placeShip(VRML_Model newmodel, int newx, int newy, int newlength, int neworientation) {
		if (newx < 0 || newy < 0 || newlength == 0) return null;
		if (!shipAvailable(newlength)) return null;
		
		int coord = toGridCoord(newx, newy);
		int add = 0;
		if (!touching) add++;
		
		for (int i=0; i<newlength; i++) {
			int c = coord + i*steps[neworientation];
			if (c < 0 || c >= occupation.length) {
				return null;
			} else if (occupation[c] != FREE) {
				return null;
			}
		}
		
		int name = freenames.remove(0);
		
		for (int i=-add; i<newlength+add; i++) {
			for (int j=-add; j<add+1; j++) {
				int c = coord + i*steps[neworientation] + j*steps[Grid.ORIENTATION_SWITCH - neworientation];
				if (c >= 0 && c < occupation.length) {
					if (occupation[c] != OUTSIDE) {
						occupation[c] = name;
					}
				}	
			}
		}		
			
		Ship ship = new Ship(newmodel, newx, newy, newlength, neworientation);
		shiplist.add(ship);
		ship.setName(name);
		checkSelectedShip();	
		return ship;
	}
	
	public void removeShip(int name) {
		for (int i=0; i<shiplist.size(); i++) {
			if (shiplist.get(i).getName() == name) {
				shiplist.remove(i);
				break;
			}
		}
		for (int i=0; i<occupation.length; i++) {
			if (occupation[i] == name) {
				occupation[i] = FREE;
			}
		}
		freenames.add(name);
		checkSelectedShip();
	}
	
	public void checkSelectedShip() {
		if (hovership.getLength() == 0) setHoverLength(1);
		if (!shipAvailable(hovership.getLength())) {
			if (!allShipsDeployed()) {
				for (int i=1; i<=5; i++) {
					if (shipAvailable(i)) {
						setHoverLength(i);
					}
				}
			}
		}
	}
	
	public void prepareForBattle() {
		clearGrid();
		for (int i=0; i<occupation.length; i++) {
			if ((i+2)%(rows+4) < 4) {
				occupation[i] = OUTSIDE;
			} else {
				occupation[i] = FREE;
			}
		}
		for (int j=0; j<shiplist.size(); j++) {
			Ship ship = shiplist.get(j);
			int coord = toGridCoord(ship.getX(), ship.getY());
			for (int i=0; i<ship.getLength(); i++) {
				int c = coord + i*steps[ship.getOrientation()];
				if (c >= 0 && c < occupation.length) {
					if (occupation[c] != OUTSIDE) {
						occupation[c] = ship.getName();
					}
				}
			}
		}
	}
	
	public int shootAt(int coord) {
		// return: 0 = Hit water, -1 = Hit ship, else = special code;
		if (coord < 0 || coord >= occupation.length) return 0;
		if (occupation[coord] == OUTSIDE) return 0;
		if (occupation[coord] == DESTROYED) return 0;
		if (occupation[coord] == FREE) {
			gridstate[coord] = COLOR_BLUE;
			return 0;
		}
		// A ship was hit
		int name = occupation[coord];
		gridstate[coord] = COLOR_RED;
		occupation[coord] = -name;
		boolean alive = false;
		for (int i=0; i<occupation.length; i++) {
			if (occupation[i] == name) {
				alive = true;
				break;
			}
		}
		if (alive) {
			return -1;
		} else {
			for (int i=0; i<shiplist.size(); i++)  {
				Ship ship = shiplist.get(i);
				if (ship.getName() == name) { 
					ship.sink();
					int head = toGridCoord(ship.getX(), ship.getY());
					int heading = (ship.getLength()+5*ship.getOrientation());
					int result = (head | (heading << 12));
					System.out.println("Ship hit at "+head+" heading "+heading+" resulting in "+result);
					return result;
				}
			}
		}
		return 0; // This would be an error
	}
	
	public void sinkShip(int eval) {		
		int head = eval & 0xFFF;
		int heading = eval >>> 12;
		int length = heading % 5;
		if (length == 0) length = 5;
		int orientation = heading/6;
		Point pos = toGameCoord(head);
		Ship ship = new Ship(ships[length-1], pos.x, pos.y, length, orientation);
		shiplist.add(ship);
		ship.sink();
		for (int i=0; i<length; i++) {
			setGrid(head + i*steps[orientation], COLOR_RED);	
		}
	}
	
	public boolean allShipsDestroyed() {
		for (int c=0; c<occupation.length; c++) {
			if (occupation[c] > 0 && occupation[c] != OUTSIDE) {
				return false;
			}
		}
		return true;
	}
	
	public void update(double delta) {
		for (int i=0; i<shiplist.size(); i++) {
			shiplist.get(i).update(delta, this.size);
		}
	}
	
	public void renderGrid() {
		field.scale(size, size, size);		
		for (int r=0; r<rows; r++) {
			for (int c=0; c<columns; c++) {
				int coord = toGridCoord(r, c);
				GL11.glLoadName(gridoffset+coord);
				field.translate(this.x + r*this.size, 0, this.z + c*this.size);
				if (coord == gridhover && (gridhoveroverrride || gridstate[coord] == COLOR_WHITE)) {
					field.setTexture(gridtex[gridhovercolor]);
				} else {
					field.setTexture(gridtex[gridstate[coord]]);
				}
				field.render();
			}
		}
	}
	
	public void renderShips() {
		for (int i=0; i<shiplist.size(); i++) {
			Ship ship = shiplist.get(i);
			GL11.glLoadName(gridoffset+gridstate.length+ship.getName());  // Ship Name for selection			
			ship.rotate(0, angles[ship.getOrientation()], 0);
			ship.translate(this.x + (ship.getX() + 0.5f)*size, 0, this.z + (ship.getY() + 0.5f)*size);
			ship.render();
		}		
		if (hovership.getX() >= 0 && hovership.getY() >= 0 && hovership.getLength() > 0) {
			hovership.rotate(0, angles[hovership.getOrientation()], 0);
			hovership.translate(this.x + (hovership.getX() + 0.5f)*size, 0, this.z + (hovership.getY() + 0.5f)*size);
			hovership.render();
		}
	}
	
	private void createField() {
		gridtex = new Texture[5];
		gridtex[COLOR_WHITE] = TextureLoader.get().getTexture("res/textures/grid0.bmp");		
		gridtex[COLOR_GREEN] = TextureLoader.get().getTexture("res/textures/grid1.bmp");
		gridtex[COLOR_RED] = TextureLoader.get().getTexture("res/textures/grid2.bmp");
		gridtex[COLOR_ORANGE] = TextureLoader.get().getTexture("res/textures/grid3.bmp");
		gridtex[COLOR_BLUE] = TextureLoader.get().getTexture("res/textures/grid4.bmp");
		field = new VRML_Shape();
		field.addPoint(0, 0, 0);
		field.addPoint(0, 0, 1);
		field.addPoint(1, 0, 1);
		field.addPoint(1, 0, 0);
		Integer[] face = { 0, 1, 2, 3 };
		field.addFace(face);
		field.addTexture(0, 0);
		field.addTexture(0, 1);
		field.addTexture(1, 1);
		field.addTexture(1, 0);
		field.addTexFace(face);
		field.setTexture(gridtex[COLOR_WHITE]);
		field.optimize();
	}
	
}